/********************************************************************************
 * Copyright(c) 2020-2025 VINCENT_WY All rights reserved.
 * - Filename
 * - Author  Vincent
 * - Version V1.0.0
 * - Date    2023/09/21
 * - Brief
 * - FunctionList:
 ******************************************************************************
 * History:
 *
 *
 *
 ******************************************************************************
 */

#include "Replayer.h"
#include "nlohmann/json.hpp"
#include "NoBufferedReplayImpl.h"
#include <fstream>
#include "logutil.h"
#include "ModuleManager.h"

using njson = nlohmann::json;

Replayer::Replayer(const std::string &cfg, void* m)
  : _cfgFile(cfg), _module(m)
{
  LOGD("%s Create ...", Tag.c_str());
}

Replayer::~Replayer() { LOGD("%s Destroy ...", Tag.c_str()); }
void Replayer::start(
    const std::function<void(std::shared_ptr<CameraFrame> &&)> &callback,
    const std::function<void(std::shared_ptr<BevFrame> &&)> &bc)
{
  creatReplayImpl();

  _canImpl->start(_allFrameStartTime);

  for (auto &&impl : _cameraImpl)
  {
    impl->start(callback, bc, _allFrameStartTime);
  }

  _loopThread = std::make_unique<std::thread>(&Replayer::loop, this);

  LOGD("%s Start ...", Tag.c_str());
}
void Replayer::stop()
{
  _isExit = true;
  if (_loopThread != nullptr && _loopThread->joinable())
  {
    _loopThread->join();
  };

  for (auto &&impl : _cameraImpl)
  {
    impl->stop();
  }
  _canImpl->stop();
  LOGD("[Replay]  exit ...");
}

void Replayer::creatReplayImpl()
{
  std::fstream cfgStream{_cfgFile};
  std::vector<long long int> frameStartTime;
  njson cfg;
  try
  {
    cfgStream >> cfg;
    _repeat = cfg["player"]["repeat"].get<bool>();
    _rootPath = cfg["player"]["path"].get<std::string>();
    auto source = cfg.at("source");
    // camera
    for (auto &camera : source)
    {
      auto id = camera.at("camera_id").get<int>();
      auto name = camera.at("camera_name").get<std::string>();
      auto player = std::make_shared<NoBufferedReplayImpl>(id, _rootPath + "/" + name);

      if (player->getFirstFrameTm() < 0)
        continue;
      frameStartTime.emplace_back(player->getFirstFrameTm());
      _cameraImpl.push_back(player);
    }

    // can
    auto useCanMsg = cfg["can"]["use"].get<bool>();
    auto canMsgName = cfg["can"]["name"].get<std::string>();
    _canImpl = std::make_shared<NoBufferedCanMsgReplayImpl>(useCanMsg, _rootPath + "/" + canMsgName);
    if (_canImpl->getFirstFrameTm() > 0)
    {
      frameStartTime.emplace_back(_canImpl->getFirstFrameTm());
    }

    if (frameStartTime.empty())
      return;
    std::sort(frameStartTime.begin(), frameStartTime.end());
    _allFrameStartTime = frameStartTime.front();
    LOGD("all frame start time: %ld\n", _allFrameStartTime);
  }
  catch (const std::exception &e)
  {
    LOGE("%s get config from {%s,error {%s} ...", Tag.c_str(), _cfgFile.c_str(), e.what());
    return;
  }
}
void Replayer::loop()
{
  while (!_isExit)
  {
    _repeatCount = 0;
    for (auto &&impl : _cameraImpl)
    {
      if (impl->isComplete())
      {
        _repeatCount++;
      }
    }

    if (!_repeat && (_repeatCount == (int)_cameraImpl.size()) && _canImpl->isComplete())
    {
      std::thread t([this] () {
        LOGD("replay no repeat, need to quit...");
        std::this_thread::sleep_for(std::chrono::seconds(1));
        if (_module) {
          ((ModuleManager*)_module)->stop();
        } else {
          LOGD("replay loop module is nullptr!!!");
        }
      });
      t.detach();
      LOGD("replay loop quit...");
      return;
    }

    // 开启重复，每一路都回灌完成，则重新回灌
    if (_repeat && (_repeatCount == (int)_cameraImpl.size())&& _canImpl->isComplete())
    {
      _repeatCount = 0;
      LOGD("%s will do repeat,please wait for 5 second ...", Tag.c_str());
      std::this_thread::sleep_for(std::chrono::seconds(5));

      for (auto &&impl : _cameraImpl)
      {
        impl->doRepeat();
      }
      _canImpl->doRepeat();
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
}
